﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.Debugger.Interop;
using UDKDevKitVSXDebugEngine;
using UDKDevKitVSXDebugEngine.Definitions;
using System.Diagnostics;

// This file contains the various event objects that are sent to the debugger from the sample engine via IDebugEventCallback2::Event.
// These are used in EngineCallback.cs.
// The events are how the engine tells the debugger about what is happening in the debuggee process. 
// There are three base classe the other events derive from: AD7AsynchronousEvent, AD7StoppingEvent, and AD7SynchronousEvent. These 
// each implement the IDebugEvent2.GetAttributes method for the type of event they represent. 
// Most events sent the debugger are asynchronous events.


namespace UDKDevKitVSXDebugEngine
{
    #region Event base classes

    class AD7AsynchronousEvent : IDebugEvent2
    {
        public const uint Attributes = (uint)enum_EVENTATTRIBUTES.EVENT_ASYNCHRONOUS;

        int IDebugEvent2.GetAttributes(out uint eventAttributes)
        {
            eventAttributes = Attributes;
            return Constants.S_OK;
        }
    }

    class AD7StoppingEvent : IDebugEvent2
    {
        public const uint Attributes = (uint)enum_EVENTATTRIBUTES.EVENT_ASYNC_STOP;

        int IDebugEvent2.GetAttributes(out uint eventAttributes)
        {
            eventAttributes = Attributes;
            return Constants.S_OK;
        }
    }

    class AD7SynchronousEvent : IDebugEvent2
    {
        public const uint Attributes = (uint)enum_EVENTATTRIBUTES.EVENT_SYNCHRONOUS;

        int IDebugEvent2.GetAttributes(out uint eventAttributes)
        {
            eventAttributes = Attributes;
            return Constants.S_OK;
        }
    }

    class AD7SynchronousStoppingEvent : IDebugEvent2
    {
        public const uint Attributes = (uint)enum_EVENTATTRIBUTES.EVENT_STOPPING | (uint)enum_EVENTATTRIBUTES.EVENT_SYNCHRONOUS;

        int IDebugEvent2.GetAttributes(out uint eventAttributes)
        {
            eventAttributes = Attributes;
            return Constants.S_OK;
        }
    }

    class AD7ExpressionEvaluationEvent : IDebugEvent2
    {
        public const uint Attributes = (uint)enum_EVENTATTRIBUTES.EVENT_EXPRESSION_EVALUATION;

        int IDebugEvent2.GetAttributes(out uint eventAttributes)
        {
            eventAttributes = Attributes;
            return Constants.S_OK;
        }
    }

    #endregion

    // The debug engine (DE) sends this interface to the session debug manager (SDM) when an instance of the DE is created.
    sealed class AD7EngineCreateEvent : AD7AsynchronousEvent, IDebugEngineCreateEvent2
    {
        private IDebugEngine2 engine;

        AD7EngineCreateEvent(Engine engine)
        {
            this.engine = engine;
        }

        public static void Send(Engine engine)
        {
            AD7EngineCreateEvent eventObject = new AD7EngineCreateEvent(engine);
            engine.Callback.Send(eventObject, typeof(IDebugEngineCreateEvent2).GUID, null, null);
        }
        
        int IDebugEngineCreateEvent2.GetEngine(out IDebugEngine2 engine)
        {
            engine = this.engine;
            
            return Constants.S_OK;
        }
    }

    // This interface is sent by the debug engine (DE) to the session debug manager (SDM) when a program is attached to.
    sealed class AD7ProgramCreateEvent : AD7AsynchronousEvent, IDebugProgramCreateEvent2
    {
        internal static void Send(Engine engine)
        {
            AD7ProgramCreateEvent eventObject = new AD7ProgramCreateEvent();
            engine.Callback.Send(eventObject, typeof(IDebugProgramCreateEvent2).GUID, null);
        }
    }


    // This interface is sent by the debug engine (DE) to the session debug manager (SDM) when a module is loaded or unloaded.
    sealed class AD7ModuleLoadEvent : AD7AsynchronousEvent, IDebugModuleLoadEvent2
    {
        readonly IDebugModule2 m_module;
        readonly bool m_fLoad;

        public AD7ModuleLoadEvent(IDebugModule2 module, bool fLoad)
        {
            m_module = module;
            m_fLoad = fLoad;
        }

        int IDebugModuleLoadEvent2.GetModule(out IDebugModule2 module, ref string debugMessage, ref int fIsLoad)
        {
            module = m_module;

            if (m_fLoad)
            {
                debugMessage = String.Concat("Loaded 'Module1", /*m_module.DebuggedModule.Name,*/ "'");
                fIsLoad = 1;
            }
            else
            {
                debugMessage = String.Concat("Unloaded 'Module1", /*m_module.DebuggedModule.Name,*/ "'");
                fIsLoad = 0;
            }

            return Constants.S_OK;
        }
    }

    // This interface is sent by the debug engine (DE) to the session debug manager (SDM) when a program has run to completion
    // or is otherwise destroyed.
    sealed class AD7ProgramDestroyEvent : AD7SynchronousEvent, IDebugProgramDestroyEvent2
    {
        readonly uint m_exitCode;
        public AD7ProgramDestroyEvent(uint exitCode)
        {
            m_exitCode = exitCode;
        }

        public static Guid IID { get { return typeof(IDebugProgramDestroyEvent2).GUID; } }

        #region IDebugProgramDestroyEvent2 Members

        int IDebugProgramDestroyEvent2.GetExitCode(out uint exitCode)
        {
            exitCode = m_exitCode;
            
            return Constants.S_OK;
        }

        #endregion
    }

    // This interface is sent by the debug engine (DE) to the session debug manager (SDM) when a thread is created in a program being debugged.
    sealed class AD7ThreadCreateEvent : AD7AsynchronousEvent, IDebugThreadCreateEvent2
    {
        public static Guid IID { get { return typeof(IDebugThreadCreateEvent2).GUID; } }
    }

    // This interface is sent by the debug engine (DE) to the session debug manager (SDM) when a thread has exited.
    sealed class AD7ThreadDestroyEvent : AD7AsynchronousEvent, IDebugThreadDestroyEvent2
    {
        readonly uint m_exitCode;
        public AD7ThreadDestroyEvent(uint exitCode)
        {
            m_exitCode = exitCode;
        }

        public static Guid IID { get { return typeof(IDebugThreadDestroyEvent2).GUID; } }

        #region IDebugThreadDestroyEvent2 Members

        int IDebugThreadDestroyEvent2.GetExitCode(out uint exitCode)
        {
            exitCode = m_exitCode;
            
            return Constants.S_OK;
        }

        #endregion
    }

    // This interface is sent by the debug engine (DE) to the session debug manager (SDM) when a program is loaded, but before any code is executed.
    sealed class AD7LoadCompleteEvent : AD7StoppingEvent, IDebugLoadCompleteEvent2
    {
        public AD7LoadCompleteEvent()
        {
        }

        public static Guid IID { get { return typeof(IDebugLoadCompleteEvent2).GUID; } }
    }

    sealed class AD7EntryPointEvent : AD7StoppingEvent, IDebugEntryPointEvent2
    {
        public AD7EntryPointEvent()
        {
        }

        public static Guid IID { get { return typeof(IDebugEntryPointEvent2).GUID; } }
    }

    // This interface tells the session debug manager (SDM) that an asynchronous break has been successfully completed.
    sealed class AD7AsyncBreakCompleteEvent : AD7StoppingEvent, IDebugBreakEvent2
    {
        public static Guid IID { get { return typeof(IDebugBreakEvent2).GUID; } }
    }

    // This interface is sent by the debug engine (DE) to the session debug manager (SDM) to output a string for debug tracing.
    sealed class AD7OutputDebugStringEvent : AD7AsynchronousEvent, IDebugOutputStringEvent2  
    {
        private string m_str;
        public AD7OutputDebugStringEvent(string str)
        {
            m_str = str;
        }

        #region IDebugOutputStringEvent2 Members

        int IDebugOutputStringEvent2.GetString(out string pbstrString)
        {
            pbstrString = m_str;
            return Constants.S_OK;
        }

        #endregion
    }

    // This interface is sent by the debug engine (DE) to indicate the results of searching for symbols for a module in the debuggee
    sealed class AD7SymbolSearchEvent : AD7AsynchronousEvent, IDebugSymbolSearchEvent2
    {
        private IDebugModule3 m_module;
        private string m_searchInfo;
        private uint m_symbolFlags;

        public AD7SymbolSearchEvent(IDebugModule3 module, string searchInfo, uint symbolFlags)
        {
            m_module = module;
            m_searchInfo = searchInfo;
            m_symbolFlags = symbolFlags;
        }

        public static Guid IID { get { return typeof(IDebugSymbolSearchEvent2).GUID; } }

        #region IDebugSymbolSearchEvent2 Members

        int IDebugSymbolSearchEvent2.GetSymbolSearchInfo(out IDebugModule3 pModule, ref string pbstrDebugMessage, enum_MODULE_INFO_FLAGS[] pdwModuleInfoFlags)
        {
            pModule = m_module;
            pbstrDebugMessage = m_searchInfo;
            pdwModuleInfoFlags[0] = (enum_MODULE_INFO_FLAGS)m_symbolFlags;

            return Constants.S_OK;
        }

        #endregion
    }

    // This interface is sent when a pending breakpoint has been bound in the debuggee.
    sealed class AD7BreakpointBoundEvent : AD7AsynchronousEvent, IDebugBreakpointBoundEvent2
    {
        private PendingBreakpoint pendingBreakpoint;
        private BoundBreakpoint boundBreakpoint;

        public AD7BreakpointBoundEvent(PendingBreakpoint pendingBreakpoint, BoundBreakpoint boundBreakpoint)
        {
            this.pendingBreakpoint = pendingBreakpoint;
            this.boundBreakpoint = boundBreakpoint;
        }

        public static Guid IID { get { return typeof(IDebugBreakpointBoundEvent2).GUID; } }

        #region IDebugBreakpointBoundEvent2 Members

        int IDebugBreakpointBoundEvent2.EnumBoundBreakpoints(out IEnumDebugBoundBreakpoints2 ppEnum)
        {
            IDebugBoundBreakpoint2[] boundBreakpoints = new IDebugBoundBreakpoint2[1];
            boundBreakpoints[0] = this.boundBreakpoint;
            ppEnum = new AD7BoundBreakpointsEnum(boundBreakpoints);
            return Constants.S_OK;
        }

        int IDebugBreakpointBoundEvent2.GetPendingBreakpoint(out IDebugPendingBreakpoint2 ppPendingBP)
        {
            ppPendingBP = this.pendingBreakpoint;
            return Constants.S_OK;
        }

        #endregion
    }

    // This Event is sent when a breakpoint is hit in the debuggee
    sealed class AD7BreakpointEvent : AD7StoppingEvent, IDebugBreakpointEvent2
    {
        IEnumDebugBoundBreakpoints2 m_boundBreakpoints;

        public AD7BreakpointEvent(IEnumDebugBoundBreakpoints2 boundBreakpoints)
        {
            m_boundBreakpoints = boundBreakpoints;
        }

        public static Guid IID { get { return typeof(IDebugBreakpointEvent2).GUID; } }

        #region IDebugBreakpointEvent2 Members

        int IDebugBreakpointEvent2.EnumBreakpoints(out IEnumDebugBoundBreakpoints2 ppEnum)
        {
            ppEnum = m_boundBreakpoints;
            return Constants.S_OK;
        }

        #endregion
    }

    // This interface is sent by the debug engine (DE) to the session debug manager (SDM) to return evalution result
    sealed class AD7DebugExpressionEvaluationCompleteEvent : AD7AsynchronousEvent, IDebugExpressionEvaluationCompleteEvent2
    {
        private DebuggerExpression expr;
        
        public AD7DebugExpressionEvaluationCompleteEvent(DebuggerExpression expr)
        {
            this.expr = expr;
        }

        public static Guid IID { get { return typeof(IDebugExpressionEvaluationCompleteEvent2).GUID; } }

        #region IDebugExpressionEvaluationCompleteEvent2 Members

        int IDebugExpressionEvaluationCompleteEvent2.GetExpression(out IDebugExpression2 ppExpr)
        {
            Debug.WriteLine("IDebugExpressionEvaluationCompleteEvent2::GetExpression");
            ppExpr = this.expr;
            return Constants.S_OK;
        }

        int IDebugExpressionEvaluationCompleteEvent2.GetResult(out IDebugProperty2 ppResult)
        {
            ppResult = null;

            Debug.WriteLine("IDebugExpressionEvaluationCompleteEvent2::GetResult");            
            
            IDebugExpression2 expression = this.expr as IDebugExpression2;
            if (expression != null)
            {
                return expression.EvaluateSync(enum_EVALFLAGS.EVAL_RETURNVALUE, 0, null, out ppResult);
            }

            return Constants.E_FAIL;
        }

        #endregion
    }
}
